package com.authsphere.security.authorization;

import com.authsphere.security.common.TokenErrorAuthenticationToken;
import com.authsphere.security.common.TokenExpiredAuthenticationToken;
import com.authsphere.security.common.TokenNotExistedAuthenticationToken;
import com.authsphere.security.token.common.AccessToken;
import com.authsphere.security.token.common.AuthenticationTokenServices;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.util.Assert;

import java.util.Objects;

/**
 * @program: AuthSphere
 * @description:
 * @author: YuKai Fan
 * @create: 2025/3/13 10:25
 **/
public class AuthorizationTokenAuthenticationProvider implements AuthenticationProvider, InitializingBean {

    private final AuthenticationTokenServices authenticationTokenServices;

    public AuthorizationTokenAuthenticationProvider(AuthenticationTokenServices authenticationTokenServices) {
        this.authenticationTokenServices = authenticationTokenServices;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        AuthorizationAuthenticationToken authorizationAuthenticationToken = (AuthorizationAuthenticationToken) authentication;
        AccessToken accessToken = authorizationAuthenticationToken.getPrincipal();
        if (Objects.isNull(accessToken)) {
            return TokenNotExistedAuthenticationToken.unauthenticated();
        }
        if (accessToken.getExpiration() == null || accessToken.isExpired()) {
            return TokenExpiredAuthenticationToken.unauthenticated();
        }
        Authentication storedAuthentication = authenticationTokenServices.getAuthentication(accessToken.getValue());
        if (Objects.isNull(storedAuthentication)) {
            return TokenErrorAuthenticationToken.unauthenticated();
        }
        AuthorizationRequest request = authorizationAuthenticationToken.getCredentials();
        if (Objects.nonNull(request)) {
            if (request.needAutoRenewal() && request.renewalInterval() > 0) {
                if (accessToken.getExpiresIn() < request.renewalInterval()) {
                    accessToken = authenticationTokenServices.refreshAccessToken(accessToken, authentication);
                }
            }
        }
        return new AuthorizationAuthenticationToken(accessToken, storedAuthentication);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return AuthorizationAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public AuthenticationTokenServices getAuthenticationTokenServices() {
        return authenticationTokenServices;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.authenticationTokenServices, "authenticationTokenServices cannot be null");
    }
}
